package com.onpositive.knowledge.model.value.audits;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.data.units.UnitKind;
import com.onpositive.knowledge.model.values.DescribedValue;
import com.onpositive.knowledge.model.values.IHasValue;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.ValueUtils;
import com.onpositive.units.ParsedDate;
import com.onpositive.units.ParsedDimension;
import com.onpositive.units.ParsedScalar;

public class ContradictionsDetectionAudit extends AuditSourceContainer  implements IValueAudit {

	/**
	 * 
	 */
	public static final String ID = "Contradiction detection audit";
	public static final String MESSAGE = "Contase objects with ambivalent values";
	private static final long serialVersionUID = 3970603794492575166L;
	private final static HashSet<String> acceptedValueTypes = new HashSet<String>( 
			Arrays.asList(new String[]{ ParsedScalar.ID, ParsedDimension.ID }) ) ;

	@Override
	public void perform(IProperty prop, Iterable<Object> objects, AbstractAuditResult auditResult ) {
		
		if( auditResult == null )
			return ;
		
		String propName = auditResult.getPropertyId() ;
		
		StatisticValueAudit sva = new StatisticValueAudit() ;
		AbstractAuditResult aar = sva.createAuditResult(prop) ;
		if( !(aar instanceof RangeAuditResult) )
			return ;
		
		RangeAuditResult rar = (RangeAuditResult) aar ;
		
		perform(prop, objects, rar) ;
		
		double range = rar.getUpperBound() - rar.getLowerBound() ;
		
		
		auditResult.setComment( ID + ". " + MESSAGE + ".") ;
		auditResult.setAuditId(ID) ;
		auditResult.setLooksApplyable(true) ;
		
		for (Object o : objects)
		{
			Object value = prop.getValue(o);
			if ( value == null )
				continue ;
			
			Iterable<Object> collection = ValueUtils.toCollection(value);
			
			if( hasContradictingValues( collection, range ) )
				auditResult.addAsSuspicious(o);

		}
		flushInfo(auditResult, prop) ;
	}



	private boolean hasContradictingValues(Iterable<Object> collection, double range) {
		
		HashSet<Double> uniqueValues = new HashSet<Double>() ;
		UnitKind kind = null ; 
		
		for( Object _value : collection )
		{
			if( _value == null || !(_value instanceof DescribedValue) )
				continue ;
			
			Object value = ((DescribedValue)_value).value() ;
			
			Iterable<Object> valColl = ValueUtils.toCollection(value);
			
			for( Object obj : valColl )
			{
				if( !(obj instanceof IHasValue ) )
					continue ;
				
				Object finalValue = ((IHasValue)obj).value() ;
				
				if( !(finalValue instanceof Number) )
					continue ;
					
				double dValue = ((Number)finalValue).doubleValue() ;
				
				if( obj instanceof ParsedDimension )
				{
					ParsedDimension pd = (ParsedDimension) obj ;
					UnitKind k = pd.getUnit().getKind() ;
					if( kind == null )
						kind = k ;
					else
						if( !kind.equals(k) )
							return true ;
				}
				uniqueValues.add( dValue ) ;				
			}
		}
		if( uniqueValues.size() < 2 )
			return false;
		
		double max = Collections.max( uniqueValues ) ;
		double min = Collections.min( uniqueValues ) ;
				
		return max - min > range * 0.05 ;		
	}
	
	private void flushInfo( AbstractAuditResult pm, IProperty prop ){
		
		Object suspicious[] = pm.getSuspicious() ;
		if( suspicious == null || suspicious.length == 0 )
			return ;		
		
		String path = "C:/mediawiki/contradictions/" ;
		new File( path ).mkdirs() ;
		String fileName = path +pm.getPropertyId().replaceAll("/", "_") + ".txt" ;
		File f = new File(fileName) ;
		
		try {
			PrintStream ps = new PrintStream( f ) ;
			HashSet<UnitKind> kindSet = new HashSet<UnitKind>() ;
			ArrayList<Double> valueList = new ArrayList<Double>() ;
			
			for( Object o : suspicious )
			{
				Object __value = prop.getValue(o);
				if ( __value == null )
					continue ;
				
				Iterable<Object> collection = ValueUtils.toCollection(__value);
				
				UnitKind kind = null ;
				boolean differentKind = false ;
				
				valueList.clear() ;
				kindSet.clear() ;				
				
				for( Object _value : collection )
				{
					if( _value == null || !(_value instanceof DescribedValue) )
						continue ;
					
					Object value = ((DescribedValue)_value).value() ;
					
					Iterable<Object> valColl = ValueUtils.toCollection(value) ;
					
					for( Object obj : valColl )
					{
						if( !(obj instanceof IHasValue ) )
							continue ;
						
						Object finalValue = ((IHasValue)obj).value() ;
						
						if( !(finalValue instanceof Number) )
							continue ;
							
						double dValue = ((Number)finalValue).doubleValue() ;
						
						if( obj instanceof ParsedDimension )
						{
							ParsedDimension pd = (ParsedDimension) obj ;
							
							UnitKind k = pd.getUnit().getKind() ;
							if( kind == null ){
								kindSet.add(kind) ;
								kind = k ;
							}
							else
								if( !kind.equals(k) ){
									kindSet.add(k) ;
									differentKind = true ;
								}

						}
						valueList.add( dValue ) ;
					}
				}
				if( differentKind )
				{
					StringBuilder comment = new StringBuilder() ;
					comment.append( "Units do not belong to the same kind: " );
					for( UnitKind k : kindSet )
						comment.append( k + ", ") ;
							
					ps.print( comment.toString() ) ;
					ps.println( "\n" ) ;
				}
				
				ps.print( "values: " ) ;
				for( double d : valueList )
					ps.print( d + ", " );
					
				ps.println( "\n" ) ;				
				ps.println( "\n" ) ;
			}

			ps.close() ;
				
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}



	@Override
	public AbstractAuditResult createAuditResult(IProperty prop) {
		
		String valueType = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_PARSER_ID_LABEL, String.class, null ) ;
		if( !acceptedValueTypes.contains( valueType ) )
			return null ;
		
		StatisticValueAudit sva = new StatisticValueAudit() ;
		AbstractAuditResult aar = sva.createAuditResult(prop) ;
		if( !(aar instanceof RangeAuditResult) )
			return null ;
		
		return new AbstractAuditResult(prop){

			private static final long serialVersionUID = -6747389348796451207L;
			
		};
	}



	@Override
	public Set<String> getRequiredAudits() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public String getId() {
		return ID;
	}

}
